package it.fuesi.bubblekids.views;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import it.fuesi.bubblekids.R;
import it.fuesi.bubblekids.activities.MainActivity;
import it.fuesi.bubblekids.model.BubbleConfig;

/**
 * Created by pascotto on 07/11/14.
 */
public class CirclesDrawingView extends View {

    private static final String TAG = "CirclesDrawingView";

    private Bitmap mBitmap = null;

    private Rect mMeasuredRect;

    private int score;

    private boolean play = false;

    private int mBottom;
    private int mTop;
    private int mLeft;
    private int mRight;

    /** All available circles */
    private Map<Integer, CircleArea> mCircles = new HashMap<Integer, CircleArea>(10);
    private SparseArray<CircleArea> mCirclePointer = new SparseArray<CircleArea>(10);


    private OnCirclesDestroyed mCircleDestroyedListener;
    private OnScoreUpdated mScoreUpdatedListener;





    /**
     * Default constructor
     *
     * @param ct {@link android.content.Context}
     */
    public CirclesDrawingView(final Context ct) {
        super(ct);

        init(ct);
    }

    public CirclesDrawingView(final Context ct, final AttributeSet attrs) {
        super(ct, attrs);

        init(ct);


    }

    public CirclesDrawingView(final Context ct, final AttributeSet attrs, final int defStyle) {
        super(ct, attrs, defStyle);

        init(ct);
    }

    public void init(final Context ct) {
        // Generate bitmap used for background
        h = new Handler();
        mBitmap = null;

    }

    public synchronized void addCircle(CircleArea circle){
        circle.id = mCircles.size();

        mCircles.put(circle.id, circle);
    }

    public int getScore(){
        return this.score;
    }

    public boolean isPlay() {
        return play;
    }

    public void setPlay(boolean play) {
        this.play = play;
        if(play) {
            this.score = 0;
            h.postDelayed(r, FRAME_RATE);
        }
    }


    public interface OnScoreUpdated{
        public void onEvent(int newScore);
    }

    public interface OnCirclesDestroyed{
        public void onEvent();
    }

    public void setOnScoreUpdatedEventListener(OnScoreUpdated eventListener){
        mScoreUpdatedListener = eventListener;
    }

    public void setCirclesDestroyedEventListener(OnCirclesDestroyed eventListener) {
        mCircleDestroyedListener=eventListener;
    }

    public int getBottomValue(){
        return mBottom;
    }

    public int getTopValue(){
        return mTop;
    }

    public int getLeftValue(){
        return mLeft;
    }

    public int getRightValue(){
        return mRight;
    }




    private void createBackground(){
        final BubbleConfig config = MainActivity.getConfig();
        if(config != null){
            mBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
                    Bitmap.Config.ARGB_8888);

            Canvas c = new Canvas(mBitmap);

            int[] rgb = ColorPreference.fromHex(config.getBgColor());

            c.drawColor(Color.argb(255, rgb[0], rgb[1], rgb[2]));

        }else{
            mBitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.ic_launcher);
        }
    }

    private GradientDrawable mBubbleDrawable;

    private void createBubbleDrawable(){
        mBubbleDrawable = (GradientDrawable)getResources().getDrawable(R.drawable.bubble);
    }


    private int xVelocity = 10;
    private int yVelocity = 5;
    private Handler h;
    private final int FRAME_RATE = 30;


    @Override
    public void onDraw(final Canvas canv) {
        // background bitmap to cover all area

        if(mBitmap == null) createBackground();

        if(mBubbleDrawable == null) createBubbleDrawable();

        canv.drawBitmap(mBitmap, null, mMeasuredRect, null);

        mBottom = canv.getClipBounds().bottom;
        mTop = canv.getClipBounds().top;
        mLeft = canv.getClipBounds().left;
        mRight = canv.getClipBounds().right;


        for (CircleArea circle : mCircles.values()) {

            int x = circle.centerX;
            int y = circle.centerY;
            int width = circle.radius;
            int height = circle.radius;

            mBubbleDrawable.setBounds(x, y, x + width, y + height);
            mBubbleDrawable.setColors(circle.colors);
            mBubbleDrawable.setStroke(2, circle.strokeColor);
            mBubbleDrawable.draw(canv);

            if (circle.centerX >= mRight) {
                circle.reachedRightBoundaries();
            }
            if (circle.centerX <= mLeft) {
                circle.reachedLeftBoundaries();
            }
            if (circle.centerY >= mBottom) {
                circle.reachedBottomBoundaries();
            }
            if (circle.centerY <= mTop) {
                circle.reachedTopBoundaries();
            }

            circle.move();
        }

        if(play)
           h.postDelayed(r, FRAME_RATE);

    }


    private Runnable r = new Runnable() {
        @Override
        public void run() {
            invalidate();
        }
    };



    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        boolean handled = false;

        if(!play) return false;


        CircleArea touchedCircle;
        int xTouch;
        int yTouch;
        int pointerId;
        int actionIndex = event.getActionIndex();

        // get touch event coordinates and make transparent circle from it
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                // it's the first pointer, so clear all existing pointers data
                clearCirclePointer();

                xTouch = (int) event.getX(0);
                yTouch = (int) event.getY(0);

                // check if we've touched inside some circle
                touchedCircle = getTouchedCircle(xTouch, yTouch);

                if(touchedCircle != null) {
                    score += touchedCircle.score;
                    mScoreUpdatedListener.onEvent(score);
                    mCircles.remove(touchedCircle.id);
                    invalidate();
                }
                //touchedCircle.centerX = xTouch;
                //touchedCircle.centerY = yTouch;
                //mCirclePointer.put(event.getPointerId(0), touchedCircle);




                handled = true;

                if(mCircles.size() == 0)
                    mCircleDestroyedListener.onEvent();

                break;

            case MotionEvent.ACTION_POINTER_DOWN:
               /* Log.w(TAG, "Pointer down");
                // It secondary pointers, so obtain their ids and check circles
                pointerId = event.getPointerId(actionIndex);

                xTouch = (int) event.getX(actionIndex);
                yTouch = (int) event.getY(actionIndex);

                // check if we've touched inside some circle
                touchedCircle = obtainTouchedCircle(xTouch, yTouch);

                mCirclePointer.put(pointerId, touchedCircle);
                touchedCircle.centerX = xTouch;
                touchedCircle.centerY = yTouch;
                invalidate();
                handled = true;*/
                break;

            case MotionEvent.ACTION_MOVE:
               /* final int pointerCount = event.getPointerCount();

                Log.w(TAG, "Move");

                for (actionIndex = 0; actionIndex < pointerCount; actionIndex++) {
                    // Some pointer has moved, search it by pointer id
                    pointerId = event.getPointerId(actionIndex);

                    xTouch = (int) event.getX(actionIndex);
                    yTouch = (int) event.getY(actionIndex);

                    touchedCircle = mCirclePointer.get(pointerId);

                    if (null != touchedCircle) {
                        touchedCircle.centerX = xTouch;
                        touchedCircle.centerY = yTouch;
                    }
                }
                invalidate();
                handled = true;*/
                break;

            case MotionEvent.ACTION_UP:
                /*clearCirclePointer();
                invalidate();
                handled = true;*/
                break;

            case MotionEvent.ACTION_POINTER_UP:
                // not general pointer was up
               /* pointerId = event.getPointerId(actionIndex);

                mCirclePointer.remove(pointerId);
                invalidate();
                handled = true;*/
                break;

            case MotionEvent.ACTION_CANCEL:
                handled = true;
                break;

            default:
                // do nothing
                break;
        }

        return super.onTouchEvent(event) || handled;
    }

    /**
     * Clears all CircleArea - pointer id relations
     */
    private void clearCirclePointer() {
        Log.w(TAG, "clearCirclePointer");

        mCirclePointer.clear();
    }

    /**
     * Search and creates new (if needed) circle based on touch area
     *
     * @param xTouch int x of touch
     * @param yTouch int y of touch
     *
     * @return obtained {@link CircleArea}
     */
    private CircleArea obtainTouchedCircle(final int xTouch, final int yTouch) {
        CircleArea touchedCircle = getTouchedCircle(xTouch, yTouch);

        /*
        if (null == touchedCircle) {
            touchedCircle = new CircleArea(xTouch, yTouch, mRadiusGenerator.nextInt(RADIUS_LIMIT) + RADIUS_LIMIT);

            if (mCircles.size() == CIRCLES_LIMIT) {
                Log.w(TAG, "Clear all circles, size is " + mCircles.size());
                // remove first circle
                mCircles.clear();
            }

            Log.w(TAG, "Added circle " + touchedCircle);
            mCircles.add(touchedCircle);
        }*/

        return touchedCircle;
    }

    /**
     * Determines touched circle
     *
     * @param xTouch int x touch coordinate
     * @param yTouch int y touch coordinate
     *
     * @return {@link CircleArea} touched circle or null if no circle has been touched
     */
    private synchronized  CircleArea getTouchedCircle(final int xTouch, final int yTouch) {
        CircleArea touched = null;

        for (CircleArea circle : mCircles.values()) {
            if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + (circle.centerY - yTouch) * (circle.centerY - yTouch) <= circle.radius * circle.radius) {
                touched = circle;
                break;
            }
        }

        return touched;
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        mMeasuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
    }
}
